﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GeometryData = LightCAD.MathLib.GeometryData;

namespace LightCAD.Core.Element3d
{
    public class Stair3d : Solid3d
    {
        public Surface3d[] Surfaces;
        private TopoFaceModel topoFaceModel;
        private double stepHeight;
        private double stepWidth;
        private int stepNum;
        private double ladderWidth;
        private double platWidth;
        private double platLen;
        private double platThickness;
        private double slabThickness;
        public double StepHeight { get => stepHeight; set => SetSize(value, stepWidth, stepNum, ladderWidth, platWidth, platLen, platThickness, slabThickness); }
        public double StepWidth { get => stepWidth; set => SetSize(stepHeight, value, stepNum, ladderWidth, platWidth, platLen, platThickness, slabThickness); }
        public int StepNum { get => stepNum; set => SetSize(stepHeight, stepWidth, value, ladderWidth, platWidth, platLen, platThickness, slabThickness); }
        public double LadderWidth { get => ladderWidth; set => SetSize(stepHeight, stepWidth, stepNum, value, platWidth, platLen, platThickness, slabThickness); }
        public double PlatWidth { get => platWidth; set => SetSize(stepHeight, stepWidth, stepNum, ladderWidth, value, platLen, platThickness, slabThickness); }
        public double PlatLen { get => platLen; set => SetSize(stepHeight, stepWidth, stepNum, ladderWidth, platWidth, value, platThickness, slabThickness); }
        public double PlatThickness { get => platThickness; set => SetSize(stepHeight, stepWidth, stepNum, ladderWidth, platWidth, platLen, value, slabThickness); }
        public double SlabThickness { get => slabThickness; set => SetSize(stepHeight, stepWidth, stepNum, ladderWidth, platWidth, platLen, platThickness, value); }
        public Stair3d() { }
        public Stair3d(  double stepHeight, double stepWidth, int stepNum, double ladderWidth, double platWidth, double platLen, double platThickness,  double slabThickness)
        {
            this.SetSize( stepHeight,  stepWidth, stepNum,  ladderWidth,  platWidth,  platLen,  platThickness,  slabThickness);
        }

        public void SetSize(double stepHeight, double stepWidth, int stepNum, double ladderWidth, double platWidth, double platLen, double platThickness, double slabThickness)
        {
            if (topoFaceModel != null && (this.stepHeight != stepHeight || this.stepWidth != stepWidth || this.stepNum != stepNum || this.ladderWidth != ladderWidth ||
                this.platWidth != platWidth || this.platLen != platLen|| this.platThickness != platThickness || this.slabThickness != slabThickness))
            {
                topoFaceModel = null;
            }
            this.stepHeight =stepHeight;
            this.stepWidth =stepWidth;
            this.stepNum = stepNum;
            this.ladderWidth = ladderWidth;
            this.platWidth =platWidth;
            this.platLen =platLen;
            this.platThickness =platThickness;
            this.slabThickness =slabThickness;
                
        }
        public override TopoFaceModel CreateTopoModel()
        {
            return null;
        }
        public override Solid3d CreateMesh()
        {
            try
            {
                var laddergeo = CreateLadder(this.StepHeight, this.StepNum, this.StepWidth, this.LadderWidth, this.SlabThickness);
                var laddergeo2 = laddergeo.clone()
                    .rotateZ(-MathUtil.Pi)
                    .translateQuick(this.PlatLen, this.StepNum * this.StepWidth, this.StepNum * this.StepHeight);

                var plat = CreatePlat(this.PlatLen, this.PlatWidth, this.PlatThickness);
                plat.translateQuick(0, this.StepNum * this.StepWidth, this.StepNum * this.StepHeight);
                //var plat2 = CreatePlat(this.PlatLen, this.PlatWidth, this.PlatThickness);
                //plat2.translateQuick(0, -this.PlatWidth, 0);
                //var plat3 = plat2.clone().translateQuick(0, 0, this.StepNum * this.StepHeight * 2);
                var result = new BufferGeometry[] { laddergeo, laddergeo2, plat,/* plat2, plat3*/ };

                this.Geometry = new GeometryData();
                var posArr = new List<double>();
                var idxArr = new List<int>();
                var groups = new List<GeometryGroup>();
                int idxOffset = 0;
                for (var i = 0; i < result.Count(); i++)
                {
                    var geo = result[i];
                    geo.computeVertexNormals();
                    posArr.AddRange(geo.attributes.position.array);
                    groups.Add(new GeometryGroup()
                    {
                        Name = i + "_",
                        Start = idxArr.Count,
                        Count = geo.index.intArray.Count(),
                        MaterialIndex = 0
                    });
                    idxArr.AddRange(geo.index.intArray.Select(idx => idx + idxOffset));
                    idxOffset += geo.attributes.position.array.Length / 3;
                }
                //for (int i = 0; i < topoModel.Surfaces.Length; i++)
                //{
                //    var face = topoModel.Surfaces[i];
                //    var tuple = face.Trianglate();
                //    posArr.AddRange(tuple.Item1);
                //    groups.Add(new GeometryGroup()
                //    {
                //        Name = i + "_",
                //        Start = idxArr.Count,
                //        Count = tuple.Item2.Count(),
                //        MaterialIndex = 0
                //    });
                //    idxArr.AddRange(tuple.Item2.Select(idx => idx + idxOffset));
                //    idxOffset += tuple.Item1.Length / 3;
                //}
                this.Geometry.Groups = groups.ToArray();
                this.Geometry.Verteics = posArr.ToArray();
                this.Geometry.Indics = idxArr.ToArray();
                this.Edge = new GeometryData();
                var verteice = new List<Vector3>();
                var idxArray = new ListEx<int>() { };
                this.Edge.Verteics = Utils.GenerateVertexArr(verteice);
                this.Edge.Indics = idxArray.ToArray();
            }
            catch(Exception ex)
            {

            }
           
            return this;
        }
        //public override LightCAD.Three.BufferGeometry[] CreateGeometry3D(LcLevel level, LcElement element, Matrix3 matrix)
        //{
        //    var stair = element as QdStairRef;
        //    var laddergeo = CreateLadder(this.StepHeight, this.StepNum, this.StepWidth, this.LadderWidth, this.SlabThickness);
        //    var laddergeo2 = laddergeo.clone()
        //        .rotateZ(-MathUtil.Pi)
        //        .translateQuick(this.PlatLen, this.StepNum * this.StepWidth, this.StepNum * this.StepHeight);

        //    var plat = CreatePlat(this.PlatLen, this.PlatWidth, this.PlatThickness);
        //    plat.translateQuick(0, this.StepNum * this.StepWidth, this.StepNum * this.StepHeight);
        //    var plat2 = CreatePlat(this.PlatLen, this.PlatWidth, this.PlatThickness);
        //    plat2.translateQuick(0, -this.PlatWidth, 0);
        //    var plat3 = plat2.clone().translateQuick(0, 0, this.StepNum * this.StepHeight * 2);
        //    var result = new BufferGeometry[] { laddergeo, laddergeo2, plat, plat2, plat3 };
        //    matrix = matrix * this.Matrix;
        //    foreach (var geo in result)
        //    {
        //        GeoUtil.GeoApplyMatrix3d(geo, matrix);
        //        geo.computeVertexNormals();
        //    }
        //    return result;
        //}
        private LightCAD.Three.BufferGeometry CreateLadder(double stepHeight, int stepNum, double stepWidth, double ladderLen, double slabThickness)
        {
            var poly = new ListEx<Vector2>();
            for (int i = 0; i < stepNum; i++)
            {
                var x = i * stepWidth;
                var y = i * stepHeight;
                poly.Push(new Vector2(x, y), new Vector2(x, y + stepHeight));
            }
            poly.Push(new Vector2(stepNum * stepWidth, stepNum * stepHeight));
            var p0 = poly[0];
            var p2 = poly[2];
            var obliqueDir = p2.Clone().Sub(p0).Normalize();
            var offsetDir = obliqueDir.Clone().RotateAround(new Vector2(), -Math.PI / 2);
            var sp = new Vector2().AddScaledVector(offsetDir, slabThickness);
            sp = CurveUtil.LineIntersectLine2D(sp, obliqueDir, p0, new Vector2(1, 0));
            var ep = CurveUtil.LineIntersectLine2D(sp, obliqueDir, poly.Last(), new Vector2(0, -1));
            poly.Push(ep, sp);
            poly.Reverse();
            var shape = new LightCAD.Three.Shape(poly);
            var coodMat = new Matrix4();
            coodMat.MakeBasis(new Vector3(0, 1, 0), new Vector3(0, 0, 1), new Vector3(1, 0, 0));
            return GeoUtil.GetStretchGeometryData(shape, coodMat, ladderLen, 0).GetBufferGeometry();
        }

        private LightCAD.Three.BufferGeometry CreatePlat(double len, double width, double thickness)
        {
            var shape = ShapeUtil.Rect(0, 0, len, width);
            var coodMat = new Matrix4();
            coodMat.MakeBasis(new Vector3(1, 0, 0), new Vector3(0, 1, 0), new Vector3(0, 0, 1));
            return GeoUtil.GetStretchGeometryData(shape, coodMat, 0, -thickness).GetBufferGeometry();
        }
    }
}
